package utils

import (
	"bufio"
	"bytes"
	"fmt"
	"github.com/mitchellh/go-homedir"
	"math/rand"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"runtime"
	"strconv"
	"sync"
	"time"
)

var onceRandSeeding = new(sync.Once)

// ShellExec 执行命令
func ShellExec(cmdBin string, args ...string) (ret []string, err error) {
	err = ShellExecWalk(func(s string) error {
		ret = append(ret, s)
		return nil
	}, cmdBin, args...)
	return
}

// ShellExecWalk 逐行处理命令执行结果
func ShellExecWalk(fn func(s string) error, cmdBin string, args ...string) error {
	var out bytes.Buffer
	cmd := exec.Command(cmdBin, args...)
	cmd.Stdout = &out
	if err := cmd.Run(); err != nil {
		return err
	}
	sc := bufio.NewScanner(&out)
	for sc.Scan() {
		if err := fn(sc.Text()); err != nil {
			return err
		}
	}
	return nil
}

// RandInt 获得随机数
func RandInt(i int) int {
	if i <= 0 {
		i = 10
	}
	onceRandSeeding.Do(func() {
		rand.New(rand.NewSource(time.Now().UnixNano()))
	})
	return rand.Intn(i)
}

// LocalIp 获取内网 IP
func LocalIp() string {
	as, err := net.InterfaceAddrs()
	if err != nil {
		return ""
	}
	for _, address := range as {
		if in, ok := address.(*net.IPNet); ok && !in.IP.IsLoopback() {
			if in.IP.To4() != nil {
				return in.IP.String()
			}
		}
	}
	return ""
}

// InArray needle 是否存在于 haystack 中
func InArray(needle interface{}, haystack interface{}) (isIn bool, err error) {
	val := reflect.ValueOf(haystack)
	switch val.Kind() {
	case reflect.Slice, reflect.Array:
		for i := 0; i < val.Len(); i++ {
			if reflect.DeepEqual(needle, val.Index(i).Interface()) {
				isIn = true
				return
			}
		}
		return
	case reflect.Map:
		for _, k := range val.MapKeys() {
			if reflect.DeepEqual(needle, val.MapIndex(k).Interface()) {
				isIn = true
				return
			}
		}
		return
	default:
		err = fmt.Errorf("haystack: haystack must be slice, array or map")
		return
	}
}

// InterfaceToInt interface 转为 int
func InterfaceToInt(v interface{}) (ret int) {
	ret = int(InterfaceToInt64(v))
	return
}

// InterfaceToInt64 interface 转为 int64
func InterfaceToInt64(v interface{}) (ret int64) {
	ret, _ = strconv.ParseInt(InterfaceToStr(v), 10, 64)
	return
}

// InterfaceToFloat64 interface 转为 float64
func InterfaceToFloat64(v interface{}) (ret float64) {
	ret, _ = strconv.ParseFloat(InterfaceToStr(v), 64)
	return
}

// InterfaceToStr interface 转为 string
func InterfaceToStr(v interface{}) (ret string) {
	if v == nil {
		return
	}
	var ok bool
	if ret, ok = v.(string); ok {
		return
	} else {
		ret = fmt.Sprintf("%v", v)
	}
	return
}

// VarDump 打印变量
func VarDump(v ...interface{}) {
	_, f, l, _ := runtime.Caller(1)
	fmt.Printf("----- %s : %d @ %s -----\n", f, l, TimeNowNice())
	for _, item := range v {
		fmt.Printf("%+v\n", item)
	}
}

// GetBinPath 取可执行程序的绝对路径
func GetBinPath() (string, error) {
	return filepath.Abs(filepath.Dir(os.Args[0]))
}

// GetHomeDir 获取当前用户的Home根目录路径
func GetHomeDir() (string, error) {
	return homedir.Dir()
}
